﻿namespace VividKit.PresentationModel.Navigation
{
    using System;
    using System.Windows;
    using System.Windows.Navigation;
    using VividKit.ComponentModel;

    internal class DesktopNavigator : Disposable
    {
        private readonly object extraData;
        private readonly NavigationService navigationService;
        private readonly INavigable source;
        private readonly string targetView;
        private readonly Type targetType;

        public DesktopNavigator(NavigationService navigationService, INavigable source, Type targetType, string targetView, object extraData)
        {
            this.navigationService = navigationService;
            this.source = source;
            this.targetType = targetType;
            this.targetView = targetView;
            this.extraData = extraData;
        }

        public void Navigate()
        {
            SubscribeToNavigationEvents();

            var contentUri = NavigableAttribute.GetContentUri(this.targetView, this.targetType, this.extraData);
            var navigationStarted = this.navigationService.Navigate(contentUri, this.extraData);

            if (navigationStarted == false)
            {
                UnsubscribeFromNavigationEvents();
            }
        }

        protected override void DisposeManagedObjects()
        {
            UnsubscribeFromNavigationEvents();
        }

        private void HandleFrameNavigated(object sender, NavigationEventArgs e)
        {
            UnsubscribeFromNavigationEvents();

            this.source.OnNavigatedFrom(e.ExtraData);

            var element = e.Content as FrameworkElement;
            if (element != null)
            {
                RoutedEventHandler elementLoadedHandler = null;
                elementLoadedHandler = delegate
                {
                    element.Loaded -= elementLoadedHandler;

                    var target = element.DataContext as INavigable;
                    if (target != null)
                    {
                        target.OnNavigatedTo(e.ExtraData);
                    }
                };

                if (element.DataContext != null)
                {
                    elementLoadedHandler(element, null);
                }
                else
                {
                    element.Loaded += elementLoadedHandler;
                }
            }
        }

        private void HandleOtherFrameNavigationEvents(object sender, EventArgs e)
        {
            UnsubscribeFromNavigationEvents();
        }

        private void SubscribeToNavigationEvents()
        {
            this.navigationService.Navigated += HandleFrameNavigated;
            this.navigationService.NavigationFailed += HandleOtherFrameNavigationEvents;
            this.navigationService.NavigationStopped += HandleOtherFrameNavigationEvents;
        }

        private void UnsubscribeFromNavigationEvents()
        {
            this.navigationService.Navigated -= HandleFrameNavigated;
            this.navigationService.NavigationFailed -= HandleOtherFrameNavigationEvents;
            this.navigationService.NavigationStopped -= HandleOtherFrameNavigationEvents;
        }
    }
}